/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Single Service Worker Class
 *		Provides a threaded mechanism for performing service-related operations.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.Threading;
using System.ServiceProcess;
using System.Windows.Forms;

namespace Adaptive.Foundation.SysTools
{
    /// <summary>
    /// Provides a threaded mechanism for performing service-related operations.
    /// </summary>
    /// <remarks>
    /// This class provides independently-threaded service operations and also provides
    /// the base implementation for the Multi-Service Worker class.
    /// </remarks>
    public class SingleServiceWorker : DisposableObjectBase
    {
        /*--------------------------------------------------------------------------------
         * Public Events
         *------------------------------------------------------------------------------*/
        #region Public Events
        /// <summary>
        /// Occurs when a service is being started.
        /// </summary>
        public event EventHandler ServiceStarting;
        /// <summary>
        /// Occurs when a services has been started successfully.
        /// </summary>
        public event EventHandler ServiceStarted;
        /// <summary>
        /// Occurs when a service fails to start.
        /// </summary>
        public event EventHandler ServiceStartFailed;
        /// <summary>
        /// Occurs when a service is being stopped.
        /// </summary>
        public event EventHandler ServiceStopping;
        /// <summary>
        /// Occurs when a service has been stopped successfully.
        /// </summary>
        public event EventHandler ServiceStopped;
        /// <summary>
        /// Occurs when a service fails to stop.
        /// </summary>
        public event EventHandler ServiceStopFailed;
        /// <summary>
        /// Occurs when a service is being paused.
        /// </summary>
        public event EventHandler ServicePausing;
        /// <summary>
        /// Occurs when a service is successfully paused.
        /// </summary>
        public event EventHandler ServicePaused;
        /// <summary>
        /// Occurs when a service fails to pause execution.
        /// </summary>
        public event EventHandler ServcePauseFailed;
        /// <summary>
        /// Occurs when an exception is encountered.
        /// </summary>
        public event EventHandler ServiceException;
        /// <summary>
        /// Occurs to query the UI when an operation will affect dependent services.
        /// </summary>
        public event DependentEventHandler DependentServicesAffected;
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations 
        /// <summary>
        /// Service instancxe to execute an operation on.
        /// </summary>
        private ServiceControllerEx _itemToOperateOn;
        /// <summary>
        /// Internal execution thread.
        /// </summary>
        private Thread _executionThread;
        /// <summary>
        /// Internal time span instance.
        /// </summary>
        private TimeSpan _twentySeconds;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <value>
        /// This is the default constructor.
        /// </value>
        /// <param name="service">
        /// A <see cref="ServiceControllerEx"/> instance to be operated on.
        /// </param>
        public SingleServiceWorker(ServiceControllerEx service)
        {
            _itemToOperateOn = service;
            _twentySeconds = new TimeSpan(0, 0, 20);
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            _executionThread = null;
            _itemToOperateOn = null;
            base.Dispose(disposing);
        }
        /// <summary>
        /// Sets the internal service reference.
        /// </summary>
        /// <remarks>
        /// This method is intended for use by the <see cref="MultiServiceWorker"/> implementation
        /// to allow it to operation on multiple items.
        /// </remarks>
        /// <param name="service">
        /// A <see cref="ServiceControllerEx"/> instance to be operated upon.
        /// </param>
        protected void BaseSetService(ServiceControllerEx service)
        {
            _itemToOperateOn = service;
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets a value indicating whether the object is executing an operation.
        /// </summary>
        /// <value>
        /// <b>true</b> if an operation is currently in progress; otherwise,
        /// <b>false</b>.
        /// </value>
        public bool Executing
        {
            get { return ((_executionThread != null) && (_executionThread.IsAlive)); }
        }
        /// <summary>
        /// Gets the display name of the service being operated on.
        /// </summary>
        public string ServiceName
        {
            get
            {
                if (_itemToOperateOn != null)
                    return _itemToOperateOn.DisplayName;
                else
                    return string.Empty;
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods
        /// <summary>
        /// Raises the <see cref="ServiceStarting"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceStarting(EventArgs e)
        {
            if (ServiceStarting != null)
                ServiceStarting(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServiceStarted"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceStarted(EventArgs e)
        {
            if (ServiceStarted != null)
                ServiceStarted(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServiceStartFailed"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceStartFailed(EventArgs e)
        {
            if (ServiceStartFailed != null)
                ServiceStartFailed(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServiceStopping"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceStopping(EventArgs e)
        {
            if (ServiceStopping != null)
                ServiceStopping(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServiceStopped"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceStopped(EventArgs e)
        {
            if (ServiceStopped != null)
                ServiceStopped(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServiceStopFailed"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceStopFailed(EventArgs e)
        {
            if (ServiceStopFailed != null)
                ServiceStopFailed(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServicePausing"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServicePausing(EventArgs e)
        {
            if (ServicePausing != null)
                ServicePausing(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServicePaused"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServicePaused(EventArgs e)
        {
            if (ServicePaused != null)
                ServicePaused(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServcePauseFailed"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServicePauseFailed(EventArgs e)
        {
            if (ServcePauseFailed != null)
                ServcePauseFailed(this, e);
        }
        /// <summary>
        /// Raises the <see cref="ServiceException"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="EventArgs"/> event arguments instance.
        /// </param>
        protected virtual void OnServiceException(EventArgs e)
        {
            if (ServiceException != null)
                ServiceException(this, e);
        }
        /// <summary>
        /// Raises the <see cref="DependentServicesAffected"/> event.
        /// </summary>
        /// <param name="e">
        /// An <see cref="DependentEventArgs"/> event arguments instance containing
        /// the services list and operation continue or cancel flag.
        /// </param>
        protected virtual void OnDependentServicesAffected(DependentEventArgs e)
        {
            if (DependentServicesAffected != null)
                DependentServicesAffected(this, e);
        }
        /// <summary>
        /// Creates the internal thread mechanism and instantiates the correct
        /// function pointer based on the supplied status.
        /// </summary>
        /// <param name="status">
        /// A <see cref="ServiceControllerStatus"/> enumerated value indicating 
        /// the desired status for the internal service to be set to.
        /// </param>
        protected virtual void InitializeThread(ServiceControllerStatus status)
        {
            InitializeThreadBase(status);
        }
        /// <summary>
        /// Executes the thread that pauses the specified service.
        /// </summary>
        protected virtual void ExecutePauseThread()
        {

            if (_itemToOperateOn != null)
            {
                //Raise status event.
                OnServicePausing(EventArgs.Empty);
                //Allow UI to update property.
                Thread.Sleep(250);
                Application.DoEvents();

                //Check ability first.
                if (_itemToOperateOn.CanPauseAndContinue)
                {
                    //Attempt to perform operation.
                    try
                    {
                        _itemToOperateOn.Pause();
                        _itemToOperateOn.WaitForStatus(ServiceControllerStatus.Paused, _twentySeconds);
                    }
                    catch
                    {
                        OnServiceException(EventArgs.Empty);
                    }

                    //Raise related status event.
                    _itemToOperateOn.Refresh();
                    if (_itemToOperateOn.Status == ServiceControllerStatus.Paused)
                        OnServicePaused(EventArgs.Empty);
                    else
                        OnServicePauseFailed(EventArgs.Empty);
                }
                else
                {
                    OnServicePauseFailed(EventArgs.Empty);
                }
            }

            else
            {
                //Raise status events.
                OnServiceException(EventArgs.Empty);
                OnServicePauseFailed(EventArgs.Empty);
            }
        }
        /// <summary>
        /// Executes the thread that starts the specified service.
        /// </summary>
        protected virtual void ExecuteStartThread()
        {
            bool continueOp = false;            //Continue operation value.

            if (_itemToOperateOn != null)
            {
                //Raise status event.
                OnServiceStarting(EventArgs.Empty);
                //Allow UI to update property.
                Thread.Sleep(250);
                Application.DoEvents();

                //Cannot start disabled services.
                if (_itemToOperateOn.StartupType != ServiceStartupType.Disabled)
                {
                    //Check for dependencies first.
                    continueOp = CheckStartDependencies();
                    if (continueOp)
                    {
                        //Attempt to perform operation.
                        try
                        {
                            _itemToOperateOn.Start();
                            _itemToOperateOn.WaitForStatus(ServiceControllerStatus.Running, _twentySeconds);
                        }
                        catch
                        {
                            OnServiceException(EventArgs.Empty);
                        }

                        //Raise related status event.
                        _itemToOperateOn.Refresh();
                        if (_itemToOperateOn.Status == ServiceControllerStatus.Running)
                            OnServiceStarted(EventArgs.Empty);
                        else
                            OnServiceStartFailed(EventArgs.Empty);
                    }
                }
                else
                    OnServiceStartFailed(EventArgs.Empty);
            }
            else
            {
                //Raise status events.
                OnServiceException(EventArgs.Empty);
                OnServiceStartFailed(EventArgs.Empty);
            }
        }
        /// <summary>
        /// Executes the thread that stops the specified service.
        /// </summary>
        protected virtual void ExecuteStopThread()
        {
            bool continueOp = false;            //Continue operation value.

            if (_itemToOperateOn != null)
            {
                //Raise status event.
                OnServiceStopping(EventArgs.Empty);
                //Allow UI to update property.
                Thread.Sleep(250);
                Application.DoEvents();

                //Ensure service can be stopped.
                if (_itemToOperateOn.CanStop)
                {
                    //Check for dependencies first.
                    continueOp = CheckStopDependencies();
                    if (continueOp)
                    {
                        //Attempt to perform operation.
                        try
                        {
                            _itemToOperateOn.Stop();
                            _itemToOperateOn.WaitForStatus(ServiceControllerStatus.Stopped, _twentySeconds);
                        }
                        catch
                        {
                            OnServiceException(EventArgs.Empty);
                        }

                        //Raise related status event.
                        _itemToOperateOn.Refresh();
                        if (_itemToOperateOn.Status == ServiceControllerStatus.Stopped)
                            OnServiceStopped(EventArgs.Empty);
                        else
                            OnServiceStopFailed(EventArgs.Empty);
                    }
                }
                else
                    OnServiceStopFailed(EventArgs.Empty);
            }
            else
            {
                //Raise status events.
                OnServiceException(EventArgs.Empty);
                OnServiceStopFailed(EventArgs.Empty);
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions

        /*--------------------------------------------------------------------------------
         * Threaded Execution Methods
         *------------------------------------------------------------------------------*/
        #region Threaded Execution Methods
        /// <summary>
        /// Begins the asynchronous service start process.
        /// </summary>
        public virtual void BeginServiceOperation(ServiceControllerStatus status)
        {
            // Initialize the internal worker thread.
            InitializeThreadBase(status);

            //Begin.
            _executionThread.Start();
        }
        /// <summary>
        /// Waits for the current operation to terminate.
        /// </summary>
        public virtual void EndServiceOperation()
        {
            //Wait for the internal thread to stop.
            while ((_executionThread != null) && (_executionThread.IsAlive))
            {
                //Wait.
                Thread.Sleep(100);

                //Allow UI to update - required for UI's Invoke() mechanism to work properly with
                //threaded events.
                Application.DoEvents();
            }
        }
        /// <summary>
        /// Aborts the current operation attempt.
        /// </summary>
        /// <remarks>
        /// Use this method sparingly as it invokes the Thread.Abort() method.
        /// </remarks>
        public virtual void AbortOperation()
        {
            if ((_executionThread != null) && (_executionThread.IsAlive))
            {
                try
                {
                    _executionThread.Abort();
                }
                catch
                {
                }
            }
            _executionThread = null;
        }
        #endregion

        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Checks to determine whether any services the current service depends on
        /// should be started first.
        /// </summary>
        /// <returns>
        /// <b>true</b> if the dependencies are OK and the operation can continue;
        /// otherwise, <b>false</b>.
        /// </returns>
        private bool CheckStartDependencies()
        {
            bool returnValue = false;           //Return value.
            DependentEventArgs evArgs = null;   //Event arguments.
            
            //Do nothing if there are no dependencies.
            if (_itemToOperateOn.ServicesDependedOn.Length > 0)
            {
                //Determine whether the services are already running.
                returnValue = true;
                foreach (ServiceController item in _itemToOperateOn.ServicesDependedOn)
                {
                    returnValue = ((item.Status == ServiceControllerStatus.Running) && returnValue);
                }

                //If dependencies are already running, no UI interaction is required.
                if (!returnValue)
                {
                    //Query UI whether or not to continue.
                    evArgs = new DependentEventArgs(_itemToOperateOn.ServicesDependedOn);
                    OnDependentServicesAffected(evArgs);

                    returnValue = evArgs.ContinueOperation;

                    if (returnValue)
                    {
                        //Attempt to start any dependent services.
                        foreach (ServiceController item in _itemToOperateOn.ServicesDependedOn)
                        {
                            if (item.Status != ServiceControllerStatus.Running)
                            {
                                try
                                {
                                    item.Start();
                                    item.WaitForStatus(ServiceControllerStatus.Running, _twentySeconds);
                                }
                                catch
                                {
                                    returnValue = false;
                                }
                            }
                        }
                    }
                    evArgs.Dispose();
                }
            }
            else
                //Return true when there are no dependencies.
                returnValue = true;

            return returnValue;
        }
        /// <summary>
        /// Checks to determine whether any services the depending on the current service
        /// should be stopped first.
        /// </summary>
        /// <returns>
        /// <b>true</b> if the dependencies are OK and the operation can continue;
        /// otherwise, <b>false</b>.
        /// </returns>
        private bool CheckStopDependencies()
        {
            bool returnValue = false;           //Return value.
            DependentEventArgs evArgs = null;   //Event arguments.

            //Do nothing if there are no dependencies.
            if (_itemToOperateOn.DependentServices.Length > 0)
            {
                //Determine whether the services are already running.
                returnValue = true;
                foreach (ServiceController item in _itemToOperateOn.DependentServices)
                {
                    returnValue = ((item.Status == ServiceControllerStatus.Stopped) && returnValue);
                }

                //If dependencies are already running, no UI interaction is required.
                if (!returnValue)
                {
                    //Query UI whether or not to continue.
                    evArgs = new DependentEventArgs(_itemToOperateOn.DependentServices);
                    OnDependentServicesAffected(evArgs);

                    returnValue = evArgs.ContinueOperation;

                    if (returnValue)
                    {
                        //Attempt to start any dependent services.
                        foreach (ServiceController item in _itemToOperateOn.DependentServices)
                        {
                            if (item.Status != ServiceControllerStatus.Stopped)
                            {
                                try
                                {
                                    item.Stop();
                                    item.WaitForStatus(ServiceControllerStatus.Stopped, _twentySeconds);
                                }
                                catch
                                {
                                    returnValue = false;
                                }
                            }
                        }
                    }
                    evArgs.Dispose();
                }
            }
            else
                //Return true when there are no dependencies.
                returnValue = true;

            return returnValue;
        }

        private void InitializeThreadBase(ServiceControllerStatus status)
        {
            //Create thread based on status.
            switch (status)
            {
                case ServiceControllerStatus.Paused:
                case ServiceControllerStatus.PausePending:
                    //Pause the service.
                    _executionThread = new Thread(new ThreadStart(ExecutePauseThread));
                    break;

                case ServiceControllerStatus.Stopped:
                case ServiceControllerStatus.StopPending:
                    //Stop the service.
                    _executionThread = new Thread(new ThreadStart(ExecuteStopThread));
                    break;

                case ServiceControllerStatus.Running:
                case ServiceControllerStatus.StartPending:
                case ServiceControllerStatus.ContinuePending:
                    //Start the service.
                    _executionThread = new Thread(new ThreadStart(ExecuteStartThread));
                    break;
            }

            _executionThread.IsBackground = true;
            if (_itemToOperateOn != null)
                _executionThread.Name = _itemToOperateOn.ServiceName + " Operation";
            _executionThread.Priority = ThreadPriority.AboveNormal;
        }

        #endregion
    }
}
